﻿/*
    This file is part of nDoctor Light

    NDoctor is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    NDoctor is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with NDoctor.  If not, see <http://www.gnu.org/licenses/>.
*/
namespace Probel.Ldoc.Dal.Services
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.Linq;
    using System.Reflection;

    using AutoMapper;

    using NHibernate.Linq;

    using Probel.Ldoc.Dal.Configuration;
    using Probel.Ldoc.Dal.Entities;
    using Probel.Ldoc.Dal.Helpers;
    using Probel.Ldoc.Dal.Incoherences;
    using Probel.Ldoc.Dal.IncoherencesFix;
    using Probel.Ldoc.Dal.Mementos;
    using Probel.Ldoc.Dal.Properties;
    using Probel.Ldoc.Services.Dto;
    using Probel.Ldoc.Services.Services;

    /// <summary>
    /// API to query the repository of nDoctor Light
    /// </summary>
    [Export(typeof(IPatientService))]
    public class PatientService : IPatientService
    {
        #region Methods

        /// <summary>
        /// Analyses and list the incoherences in the database.
        /// </summary>
        /// <returns>
        /// A list of incoherences contained in the database
        /// </returns>
        public IEnumerable<IncoherenceDto> AnalyseIncoherences()
        {
            var result = new List<IncoherenceDto>();

            using (var session = DalManager.SessionFactory.OpenSession())
            {
                foreach (var rule in this.GetRules())
                {
                    var incoherence = rule.Execute(session);
                    if (incoherence != null) { result.Add(incoherence); }
                }
            }
            return result;
        }

        /// <summary>
        /// Binds the specified tag to the specified patient.
        /// </summary>
        /// <param name="tag">The tag.</param>
        /// <param name="patient">The  patient.</param>
        public void Bind(TagDto tag, PatientCardDto patient)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var patientE = session.Get<Patient>(patient.Id);
                var tagE = (from p in session.Query<SearchTag>()
                            where p.Name.ToLower() == tag.Name.ToLower()
                            select p).First();

                patientE.SearchTags.Add(tagE);

                session.Update(patientE);
                session.Flush();
            }
        }

        /// <summary>
        /// Binds the specified profession to the specified patient.
        /// </summary>
        /// <param name="profession">The profession.</param>
        /// <param name="patient">The patient.</param>
        public void Bind(ProfessionCardDto profession, PatientCardDto patient)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var professionE = (from p in session.Query<Profession>()
                                   where p.Name.ToLower() == profession.Name.ToLower()
                                   select p).First();
                var patientE = session.Get<Patient>(patient.Id);

                patientE.Profession = professionE;

                session.Update(patientE);
                session.Flush();
            }
        }

        /// <summary>
        /// Binds the specified reputation to the specified patient.
        /// </summary>
        /// <param name="reputation">The reputation.</param>
        /// <param name="patient">The patient.</param>
        public void Bind(ReputationCardDto reputation, PatientCardDto patient)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var reputationE = (from p in session.Query<Reputation>()
                                   where p.Name.ToLower() == reputation.Name.ToLower()
                                   select p).First();
                var patientE = session.Get<Patient>(patient.Id);

                patientE.Reputation = reputationE;

                session.Update(patientE);
                session.Flush();
            }
        }

        /// <summary>
        /// Binds the specified insurance to the specified patient.
        /// </summary>
        /// <param name="insurance">The insurance.</param>
        /// <param name="patient"></param>
        /// <exception cref="System.NotImplementedException"></exception>
        public void Bind(InsuranceDto insurance, PatientCardDto patient)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var patientE = session.Get<Patient>(patient.Id);
                var insuranceE = (from p in session.Query<Insurance>()
                                  where p.Id == insurance.Id
                                  select p).First();

                patientE.Insurance = insuranceE;

                session.Update(patientE);
                session.Flush();
            }
        }

        /// <summary>
        /// Binds the specified practice to the patient.
        /// </summary>
        /// <param name="practice">The practice.</param>
        /// <param name="patient">The patient.</param>
        public void Bind(PracticeDto practice, PatientCardDto patient)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var patientE = session.Get<Patient>(patient.Id);
                var practiceE = (from p in session.Query<Practice>()
                                 where p.Id == practice.Id
                                 select p).First();

                patientE.Practice = practiceE;

                session.Update(patientE);
                session.Flush();
            }
        }

        /// <summary>
        /// Binds the specified doctor to the specified patient.
        /// </summary>
        /// <param name="doctor">The doctor.</param>
        /// <param name="patient">The patient.</param>
        public void Bind(DoctorCardDto doctor, PatientCardDto patient)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var doctorE = session.Get<Doctor>(doctor.Id);
                var patientE = session.Get<Patient>(patient.Id);

                patientE.Doctors.Add(doctorE);
                doctorE.Patients.Add(patientE);

                session.Update(patientE);
                session.Flush();
            }
        }

        /// <summary>
        /// Creates the specified folder.
        /// </summary>
        /// <param name="folder">The folder.</param>
        public void Create(RecordDrawerCardDto folder)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var entity = Mapper.Map<RecordDrawerCardDto, Tag>(folder);
                session.Save(entity);
                session.Flush();
            }
        }

        /// <summary>
        /// Creates the specified record for the specified patient.
        /// </summary>
        /// <param name="record">The record.</param>
        /// <param name="drawer">The record drawer.</param>
        /// <param name="patient">The patient card.</param>
        public void Create(RecordCardDto record, RecordDrawerCardDto drawer, PatientCardDto patient)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {

                var recordE = Mapper.Map<RecordCardDto, MedicalRecord>(record);
                var drawerE = (from t in session.Query<Tag>()
                               where t.Id == drawer.Id
                               select t).Single();
                var patientE = (from p in session.Query<Patient>()
                                where p.Id == patient.Id
                                select p).Single();
                var now = DateTime.Now;
                recordE.CreationDate = now;
                recordE.LastUpdate = now;
                recordE.Tag = drawerE;

                patientE.MedicalRecords.Add(recordE);

                session.Update(patientE);
                session.Flush();
            }
        }

        /// <summary>
        /// Creates the specified patient with the minimum information.
        /// </summary>
        /// <param name="patient">The patient.</param>
        /// <exception cref="System.NotImplementedException"></exception>
        public long Create(PatientCardDto patient)
        {
            var today = DateTime.Today;
            var patientE = Mapper.Map<PatientCardDto, Patient>(patient);
            patientE.InscriptionDate
                = patientE.LastUpdate
                = today;

            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var id = session.Save(patientE);
                session.Flush();
                return (long)id;
            }
        }

        /// <summary>
        /// Creates the specified specialisation.
        /// </summary>
        /// <param name="specialisation">The specialisation.</param>
        public void Create(SpecialisationDto specialisation)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var tag = Mapper.Map<SpecialisationDto, Tag>(specialisation);
                session.Save(tag);
                session.Flush();
            }
        }

        /// <summary>
        /// Creates the specified doctor.
        /// </summary>
        /// <param name="doctor">The doctor.</param>
        public void Create(DoctorDto doctor)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var doctorE = Mapper.Map<DoctorDto, Doctor>(doctor);
                session.Save(doctorE);
                session.Flush();
            }
        }

        /// <summary>
        /// Creates a new tag and binds it to the specified patient.
        /// </summary>
        /// <param name="tag">The tag to create.</param>
        /// <param name="patient">The patient.</param>
        public void CreateAndBind(TagDto tag, PatientCardDto patient)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var tagE = Mapper.Map<TagDto, SearchTag>(tag);
                var patientE = (from p in session.Query<Patient>()
                                where p.Id == patient.Id
                                select p).Single();

                patientE.SearchTags.Add(tagE);
                session.Update(patientE);
                session.Flush();
            }
        }

        /// <summary>
        /// Creates a new profession and binds it to the specified patient.
        /// </summary>
        /// <param name="profession">The profession to create.</param>
        /// <param name="patient">The patient.</param>
        public void CreateAndBind(ProfessionCardDto profession, PatientCardDto patient)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var professionE = Mapper.Map<ProfessionCardDto, Profession>(profession);
                var patientE = session.Get<Patient>(patient.Id);
                patientE.Profession = professionE;

                session.Update(patientE);
                session.Flush();
            }
        }

        /// <summary>
        /// Creates the and bind.
        /// </summary>
        /// <param name="reputation">The reputation.</param>
        /// <param name="patient">The patient.</param>
        public void CreateAndBind(ReputationCardDto reputation, PatientCardDto patient)
        {
            {
                using (var session = DalManager.SessionFactory.OpenSession())
                {
                    var reputationE = Mapper.Map<ReputationCardDto, Reputation>(reputation);
                    var patientE = session.Get<Patient>(patient.Id);
                    patientE.Reputation = reputationE;

                    session.Update(patientE);
                    session.Flush();
                }
            }
        }

        /// <summary>
        /// Creates a new insurance and binds it to the specified patient.
        /// </summary>
        /// <param name="insurance">The insurance to create.</param>
        /// <param name="patient"></param>
        /// <exception cref="System.NotImplementedException"></exception>
        public void CreateAndBind(InsuranceDto insurance, PatientCardDto patient)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var insuranceE = Mapper.Map<InsuranceDto, Insurance>(insurance);
                var patientE = session.Get<Patient>(patient.Id);
                patientE.Insurance = insuranceE;

                session.Update(patientE);
                session.Flush();
            }
        }

        /// <summary>
        /// Creates a new practice and binds it to the specified patient.
        /// </summary>
        /// <param name="practice">The practice to create.</param>
        /// <param name="patient">The patient.</param>
        public void CreateAndBind(PracticeDto practice, PatientCardDto patient)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var practiceE = Mapper.Map<PracticeDto, Practice>(practice);
                var patientE = session.Get<Patient>(patient.Id);
                patientE.Practice = practiceE;

                session.Update(patientE);
                session.Flush();
            }
        }

        /// <summary>
        /// Creates a new medical record with the default configuration (the title is the date of today)
        /// </summary>
        /// <param name="recordDrawer">The record drawer od the record to create.</param>
        /// <param name="patient">The patient to whom this record will be added.</param>
        public void CreateDefaultRecord(RecordDrawerDto recordDrawer, PatientCardDto patient)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var now = DateTime.Now;
                var tagE = session.Get<Tag>(recordDrawer.Id);
                var patientE = session.Get<Patient>(patient.Id);
                var recordE = new MedicalRecord
                {
                    CreationDate = now,
                    LastUpdate = now,
                    Name = now.ToShortDateString(),
                    Tag = tagE,
                };

                patientE.MedicalRecords.Add(recordE);
                session.Update(patientE);
                session.Flush();
            }
        }

        /// <summary>
        /// Adds a new macro.
        /// </summary>
        public void CreateNewMacro()
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var macro = new Macro()
                {
                    Title = string.Format(Messages.Macro_DefaultName, DateTime.Today.ToShortDateString()),
                    Expression = Messages.Macro_DefaultExpression,
                };
                session.Save(macro);
                session.Flush();
            }
        }

        /// <summary>
        /// Checks if the specified tag exists in the repository.
        /// </summary>
        /// <param name="tag">The tag.</param>
        /// <returns>
        ///   <c>True</c> if it exists; otherwise <c>False</c>
        /// </returns>
        public bool Exists(TagDto tag)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var result = (from t in session.Query<SearchTag>()
                              where t.Name.ToLower() == tag.Name.ToLower()
                              select t).Count();
                return result > 0;
            }
        }

        /// <summary>
        /// Checks if the specified profession exists in the repository.
        /// </summary>
        /// <param name="profession">The profession to check.</param>
        /// <returns>
        ///   <c>True</c> if it exists; otherwise <c>False</c>
        /// </returns>
        public bool Exists(ProfessionCardDto profession)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var result = (from p in session.Query<Profession>()
                              where p.Name.ToLower() == profession.Name.ToLower()
                              select p).Count();
                return result > 0;
            }
        }

        /// <summary>
        /// Checks if the specified reputation exists in the repository.
        /// </summary>
        /// <param name="reputation">The reputation to check.</param>
        /// <returns>
        ///   <c>True</c> if it exists; otherwise <c>False</c>
        /// </returns>
        public bool Exists(ReputationCardDto reputation)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var result = (from t in session.Query<Reputation>()
                              where t.Name.ToLower() == reputation.Name.ToLower()
                              select t).Count();
                return result > 0;
            }
        }

        /// <summary>
        /// Checks if the specified insurance exists in the repository.
        /// </summary>
        /// <param name="insurance">The insurance to check.</param>
        /// <returns>
        ///   <c>True</c> if it exists; otherwise <c>False</c>
        /// </returns>
        /// <exception cref="System.NotImplementedException"></exception>
        public bool Exists(InsuranceDto insurance)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var result = (from p in session.Query<Insurance>()
                              where p.Name.ToLower() == insurance.Name.ToLower()
                              select p).Count();
                return result > 0;
            }
        }

        /// <summary>
        /// Checks if the specified practice exists in the repository.
        /// </summary>
        /// <param name="practice">The practice to check.</param>
        /// <returns>
        ///   <c>True</c> if it exists; otherwise <c>False</c>
        /// </returns>
        public bool Exists(PracticeDto practice)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var result = (from p in session.Query<Practice>()
                              where p.Name.ToLower() == practice.Name.ToLower()
                              select p).Count();
                return result > 0;
            }
        }

        /// <summary>
        /// Expands the specified patient card. That's, load all the data of this patient
        /// </summary>
        /// <param name="patient">The patient.</param>
        /// <returns>
        /// THe expanded patient
        /// </returns>
        public ExpandedPatientDto Expand(PatientCardDto patient)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var entity = session.Get<Patient>(patient.Id);
                return Mapper.Map<Patient, ExpandedPatientDto>(entity);
            }
        }

        /// <summary>
        /// Gets the expanded card of the specified patient.
        /// </summary>
        /// <param name="patient">The patient.</param>
        /// <returns>
        /// The expanded card of the spevified patient
        /// </returns>
        /// <exception cref="System.NotImplementedException"></exception>
        public ExpandedPatientCardDto ExpandCard(PatientCardDto patient)
        {
            if (patient == null) { throw new ArgumentNullException("patient"); }

            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var result = (from p in session.Query<Patient>()
                                               .Fetch(e => e.Address)
                              where p.Id == patient.Id
                              select p).Single();

                // Fix for patient created before issue 76: if address is null
                // It's impossible to update the address with the databinding.
                if (result.Address == null) { result.Address = new Address(); }

                return Mapper.Map<Patient, ExpandedPatientCardDto>(result);
            }
        }

        /// <summary>
        /// Fixes the lower case insurances.
        /// </summary>
        public void FixLowerCaseInsurances()
        {
            new LowerCaseFix<Insurance>().Fix();
        }

        /// <summary>
        /// Fixes the lower case practices.
        /// </summary>
        public void FixLowerCasePractices()
        {
            new LowerCaseFix<Practice>().Fix();
        }

        /// <summary>
        /// Fixes the lower case professions.
        /// </summary>
        public void FixLowerCaseProfessions()
        {
            new LowerCaseFix<Profession>().Fix();
        }

        /// <summary>
        /// Fixes the lower case reputations.
        /// </summary>
        public void FixLowerCaseReputations()
        {
            new LowerCaseFix<Reputation>().Fix();
        }

        /// <summary>
        /// Replace the insurance doubloons with the specified insurance.
        /// </summary>
        /// <param name="insurance">The insurance.</param>
        public void FixSpellingInsurances(InsuranceCardDto insurance)
        {
            new InsuranceSpellingFix().Fix(insurance);
        }

        /// <summary>
        /// Replace the insurance doubloons with the specified insurance.
        /// </summary>
        /// <param name="profession">The profession.</param>
        public void FixSpellingProfessions(ProfessionCardDto profession)
        {
            new ProfessionSpellingFix().Fix(profession);
        }

        /// <summary>
        /// Replace the retputation doubloons with the specified reputation.
        /// </summary>
        /// <param name="reputation">The reputation that will replace all the others</param>
        public void FixSpellingReputations(ReputationCardDto reputation)
        {
            new ReputationSpellingFix().Fix(reputation);
        }

        /// <summary>
        /// Gets all assignable tags for the spevified patient.
        /// </summary>
        /// <param name="patient">The patient.</param>
        /// <returns>
        /// All the search tags
        /// </returns>
        public IEnumerable<TagDto> GetAllAssignableTags(PatientCardDto patient)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var notin = (from p in session.Query<Patient>()
                             select p.SearchTags into st
                             from c in st
                             select c.Id);

                var result = (from t in session.Query<SearchTag>()
                              where !notin.Contains(t.Id)
                              select t).OrderBy(e => e.Name);
                return Mapper.Map<IEnumerable<SearchTag>, IEnumerable<TagDto>>(result);
            }
        }

        /// <summary>
        /// Gets all insurances.
        /// </summary>
        /// <returns>
        /// All the insurances
        /// </returns>
        public IEnumerable<InsuranceCardDto> GetAllInsurances()
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var entities = session.Query<Insurance>()
                                      .OrderBy(e => e.Name);
                return Mapper.Map<IEnumerable<Insurance>, IEnumerable<InsuranceCardDto>>(entities);
            }
        }

        /// <summary>
        /// Gets all macro cards stored in the repository.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<MacroDto> GetAllMacro()
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var macros = session.Query<Macro>();
                return Mapper.Map<IEnumerable<Macro>, IEnumerable<MacroDto>>(macros);
            }
        }

        public IEnumerable<PictureDto> GetAllPictures()
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var result = (from p in session.Query<Picture>()
                              select p);
                return Mapper.Map<IEnumerable<Picture>, IEnumerable<PictureDto>>(result);
            }
        }

        /// <summary>
        /// Gets all practices.
        /// </summary>
        /// <returns>
        /// All the practices
        /// </returns>
        public IEnumerable<PracticeCardDto> GetAllPractices()
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var result = session.Query<Practice>()
                                    .ToList()
                                    .OrderBy(e => e.Name.RemoveDiacritics());
                return Mapper.Map<IEnumerable<Practice>, IEnumerable<PracticeCardDto>>(result);
            }
        }

        /// <summary>
        /// Gets all professions.
        /// </summary>
        /// <returns>
        /// All the professions
        /// </returns>
        public IEnumerable<ProfessionCardDto> GetAllProfessions()
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var result = session.Query<Profession>()
                                    .ToList()
                                    .OrderBy(e => e.Name.RemoveDiacritics());

                return Mapper.Map<IEnumerable<Profession>, IEnumerable<ProfessionCardDto>>(result);
            }
        }

        /// <summary>
        /// Gets all reputations.
        /// </summary>
        /// <returns>
        /// All the reputations
        /// </returns>
        public IEnumerable<ReputationCardDto> GetAllReputations()
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var result = session.Query<Reputation>()
                                    .ToList()
                                    .OrderBy(e => e.Name.RemoveDiacritics());
                return Mapper.Map<IEnumerable<Reputation>, IEnumerable<ReputationCardDto>>(result);
            }
        }

        /// <summary>
        /// Gets all specialisations from the repository.
        /// </summary>
        /// <returns>
        /// All the reputations of the repository
        /// </returns>
        public IEnumerable<SpecialisationDto> GetAllSpecialisations()
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var result = (from s in session.Query<Tag>()
                              where s.Category == TagCategory.Doctor
                              select s);
                return Mapper.Map<IEnumerable<Tag>, IEnumerable<SpecialisationDto>>(result);
            }
        }

        /// <summary>
        /// Gets the doctors that fulfil the criterion.
        /// </summary>
        /// <param name="criterion">The criterion.</param>
        /// <returns>
        /// The doctors that fulil the criterion
        /// </returns>
        public IEnumerable<DoctorCardDto> GetDoctors(SearchCriterion criterion)
        {
            return this.Get<Doctor, DoctorCardDto>(criterion, new DoctorSearcher());
        }

        /// <summary>
        /// Gets the first insurance with the exact same name.
        /// The search is NOT case sensitive but take into account
        /// the diatrics (ie: é,è,ç,à)
        /// </summary>
        /// <param name="name">The name.</param>
        /// <returns>The first insurance with the exact name or <c>NULL</c> if nothing is found</returns>
        public InsuranceDto GetFirstInsurance(string name)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var insuranceE = (from i in session.Query<Insurance>()
                                  where i.Name.ToLower() == name.ToLower()
                                  select i).FirstOrDefault();
                return Mapper.Map<Insurance, InsuranceDto>(insuranceE);
            }
        }

        /// <summary>
        /// Gets the first practice with the exact same name.
        /// The search is NOT case sensitive but take into account
        /// the diatrics (ie: é,è,ç,à)
        /// </summary>
        /// <param name="name">The name.</param>
        /// <returns>
        /// The first practice with the exact name or <c>NULL</c> if nothing is found
        /// </returns>
        public PracticeDto GetFirstPractice(string name)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var insuranceE = (from i in session.Query<Practice>()
                                  where i.Name.ToLower() == name.ToLower()
                                  select i).FirstOrDefault();
                return Mapper.Map<Practice, PracticeDto>(insuranceE);
            }
        }

        /// <summary>
        /// Gets the history of the specified medical record.
        /// </summary>
        /// <param name="record">The record.</param>
        /// <returns>
        /// The history of the medical record
        /// </returns>
        /// <exception cref="System.NotImplementedException"></exception>
        public IEnumerable<RecordStateCardDto> GetHistoryOf(RecordCardDto record)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var history = (from s in session.Query<MedicalRecord>()
                               where s.Id == record.Id
                               select s).Single();
                return Mapper.Map<IEnumerable<MedicalRecordState>, IEnumerable<RecordStateCardDto>>(history.PreviousStates);
            }
        }

        /// <summary>
        /// Gets the insurances to fix. This is the list of all doubloons
        /// </summary>
        /// <param name="name"></param>
        /// <returns>
        /// All the insurances with doubloons
        /// </returns>
        public IEnumerable<InsuranceDto> GetInsurancesDoubloons(string name)
        {
            return new InsuranceSpellingFix().GetDoubloons(name);
        }

        /// <summary>
        /// Gets the insurances to fix. That's all the insurance that have doubloons.
        /// </summary>
        /// <returns>
        /// All the insurances with doubloons
        /// </returns>
        public IEnumerable<InsuranceCardDto> GetInsurancesToFix()
        {
            return new InsuranceSpellingFix().GetIncoherences();
        }

        /// <summary>
        /// Gets the lower cased insurances.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<string> GetLowerCasedInsurances()
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                return (from i in session.Query<Insurance>().ToList()
                        where char.IsLower(i.Name[0])
                        select i.Name).ToList();
            }
        }

        /// <summary>
        /// Gets the lower cased practices.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<string> GetLowerCasedPractices()
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                return (from i in session.Query<Practice>().ToList()
                        where char.IsLower(i.Name[0])
                        select i.Name).ToList();
            }
        }

        /// <summary>
        /// Gets the lower cased professions.
        /// </summary>
        /// <returns>
        /// All the lower cased professions
        /// </returns>
        public IEnumerable<string> GetLowerCasedProfessions()
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                return (from i in session.Query<Profession>().ToList()
                        where char.IsLower(i.Name[0])
                        select i.Name).ToList();
            }
        }

        /// <summary>
        /// Gets the lower cased reputations.
        /// </summary>
        /// <returns>
        /// All the lower cased reputations
        /// </returns>
        public IEnumerable<string> GetLowerCasedReputations()
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                return (from i in session.Query<Reputation>().ToList()
                        where char.IsLower(i.Name[0])
                        select i.Name).ToList();
            }
        }

        /// <summary>
        /// Gets all the macros of the specified patient.
        /// </summary>
        /// <param name="patient">The patient card dto.</param>
        /// <returns></returns>
        /// <exception cref="System.NotImplementedException"></exception>
        public IEnumerable<MacroDto> GetMacros(PatientCardDto patient)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var patientE = session.Get<Patient>(patient.Id);
                var macrosE = (from m in session.Query<Macro>()
                               select m).ToList();

                var result = new MacroResolver(patientE).Resolve(macrosE);

                return Mapper.Map<IEnumerable<Macro>, IEnumerable<MacroDto>>(result);
            }
        }

        /// <summary>
        /// Gets the patient with the specified id.
        /// </summary>
        /// <param name="id">The identifier.</param>
        /// <returns>
        /// The patient with this identifyer or null if it doesn't exist
        /// </returns>
        public PatientCardDto GetPatient(long id)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var result = (from p in session.Query<Patient>()
                              where p.Id == id
                              select p).FirstOrDefault();
                return Mapper.Map<Patient, PatientCardDto>(result);
            }
        }

        /// <summary>
        /// Gets the patients based on the specified criterion.
        /// </summary>
        /// <param name="options">The criterion of the query to execute.</param>
        /// <returns>The patient resulting the query</returns>
        public IEnumerable<PatientCardDto> GetPatients(SearchCriterion criterion)
        {
            return this.Get<Patient, PatientCardDto>(criterion, new PatientSearcher());
        }

        /// <summary>
        /// Gets the professions to fix. This is the list of all doubloons
        /// </summary>
        /// <param name="profession"></param>
        /// <returns>
        /// All the professions with doubloons
        /// </returns>
        public IEnumerable<ProfessionCardDto> GetProfessionDoubloons(string profession)
        {
            return new ProfessionSpellingFix().GetDoubloons(profession);
        }

        /// <summary>
        /// Gets the professions to fix. That's all the professions that have doubloons.
        /// </summary>
        /// <returns>
        /// All the insurances with doubloons
        /// </returns>
        public IEnumerable<ProfessionCardDto> GetProfessionsToFix()
        {
            return new ProfessionSpellingFix().GetIncoherences();
        }

        /// <summary>
        /// Gets the summary of all the medical card grouped by tags
        /// </summary>
        /// <param name="patientCardDto">The patient from which this summary is retrieved.</param>
        /// <returns>A list of tags and the medical cards linked to them</returns>
        public RecordCabinetDto GetRecordsCabinet(PatientCardDto patient)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var current = session.Get<Patient>(patient.Id);

                var result = (from rec in current.MedicalRecords
                              orderby rec.CreationDate descending
                              group rec by new { rec.Tag.Id, rec.Tag.Name } into grp
                              select new RecordDrawerDto()
                              {
                                  Id = grp.Key.Id,
                                  Title = grp.Key.Name,
                                  Cards = Mapper.Map<IEnumerable<MedicalRecord>, IEnumerable<RecordCardDto>>(grp.ToList()),
                              }).OrderBy(e => e.Title);

                return new RecordCabinetDto(result.ToList());
            }
        }

        /// <summary>
        /// Gets all the records drawers.
        /// </summary>
        /// <returns></returns>
        /// <exception cref="System.NotImplementedException"></exception>
        public IEnumerable<RecordDrawerCardDto> GetRecordsDrawers()
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var result = (from t in session.Query<Tag>()
                              where t.Category == TagCategory.MedicalRecord
                              select t);
                var mappedResult = Mapper.Map<IEnumerable<Tag>, IEnumerable<RecordDrawerCardDto>>(result);
                return mappedResult;

            }
        }

        /// <summary>
        /// Gets the reputations to fix. This is the list of all doubloons
        /// </summary>
        /// <param name="name">The reputation.</param>
        /// <returns>
        /// All the reputation with doubloons
        /// </returns>
        public IEnumerable<ReputationCardDto> GetReputationDoubloons(string name)
        {
            return new ReputationSpellingFix().GetDoubloons(name);
        }

        /// <summary>
        /// Gets the reputations to fix. That's all the reputation that have doubloons.
        /// </summary>
        /// <returns>
        /// All the reputations with doubloons
        /// </returns>
        public IEnumerable<ReputationCardDto> GetReputationsToFix()
        {
            return new ReputationSpellingFix().GetIncoherences();
        }

        /// <summary>
        /// Gets text (RTF formatted) tof the specified medical record
        /// </summary>
        /// <param name="recordCard">The record card.</param>
        /// <returns>The medical record content</returns>
        public string GetRtfFromRecord(RecordCardDto recordCard)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var record = session.Get<MedicalRecord>(recordCard.Id);
                return record.Rtf;
            }
        }

        /// <summary>
        /// Gets RTF from the specified record state.
        /// </summary>
        /// <param name="state">The state.</param>
        /// <returns></returns>
        /// <exception cref="System.NotImplementedException"></exception>
        public string GetRtfFromRecordState(RecordStateCardDto state)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var stateE = session.Get<MedicalRecordState>(state.Id);
                return stateE.Rtf;
            }
        }

        /// <summary>
        /// Determines whether the specified patient has this tag.
        /// </summary>
        /// <param name="patient">The expanded patient dto.</param>
        /// <param name="tag">The tag.</param>
        /// <returns>
        ///   <c>True</c> if this patient has this tag. Otherwise <c>False</c>
        /// </returns>
        public bool HasTag(PatientCardDto patient, TagDto tag)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var patientE = session.Get<Patient>(patient.Id);

                return (from t in patientE.SearchTags
                        where t.Name.ToLower() == tag.Name.ToLower()
                        select t).Count() > 0;
            }
        }

        /// <summary>
        /// Determines whether the specified doctor is binded to this specified patient.
        /// </summary>
        /// <param name="doctor">The doctor.</param>
        /// <param name="toThisPatient">To this patient.</param>
        /// <returns>
        ///   <c>True</c> if the patient has already the specified doctor. Otherwise <c>False</c>
        /// </returns>
        public bool IsBinded(DoctorCardDto doctor, PatientCardDto toThisPatient)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                return (from p in session.Query<Patient>()
                        where (from d in p.Doctors
                               select d.Id).Contains(doctor.Id)
                           && p.Id == toThisPatient.Id
                        select p).Count() > 0;
            }
        }

        /// <summary>
        /// Determines whether the specified macro is valid.
        /// </summary>
        /// <param name="macro">The macro.</param>
        /// <returns></returns>
        public bool IsMacroValid(MacroDto macro)
        {
            return MacroResolver.IsValidExpression(macro.Expression);
        }

        /// <summary>
        /// Refresh the medical record card from the specified record card.
        /// </summary>
        /// <param name="record">The record.</param>
        /// <returns>
        /// A medical record card with the fresh data retrieved from the database
        /// </returns>
        /// <exception cref="System.NotImplementedException"></exception>
        public RecordCardDto RefreshCard(RecordCardDto record)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var recordE = session.Get<MedicalRecord>(record.Id);
                return Mapper.Map<MedicalRecord, RecordCardDto>(recordE, record);
            }
        }

        /// <summary>
        /// Removes the specified record.
        /// </summary>
        /// <param name="record">The record to be removed.</param>
        public void Remove(RecordCardDto record)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var recordE = session.Get<MedicalRecord>(record.Id);
                session.Delete(recordE);
                session.Flush();
            }
        }

        /// <summary>
        /// Removes the specified macro.
        /// </summary>
        /// <param name="macro">The macro.</param>
        public void Remove(MacroDto macro)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var macroE = session.Get<Macro>(macro.Id);
                session.Delete(macroE);
                session.Flush();
            }
        }

        /// <summary>
        /// Resolves the macro for the specified macro.
        /// </summary>
        /// <param name="script">The script.</param>
        /// <param name="patient">The patient.</param>
        /// <returns>
        /// The resolved macro as a text
        /// </returns>
        public string ResolveMacro(string script, PatientCardDto patient)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var patientE = session.Get<Patient>(patient.Id);

                var resolver = new MacroResolver(patientE);
                return resolver.Resolve(script);
            }
        }

        /// <summary>
        /// Saves the RTF into the specified record.
        /// </summary>
        /// <param name="record">The record.</param>
        /// <param name="rtf">The RTF.</param>
        public void SaveRtf(RecordCardDto record, string rtf)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var recordE = session.Get<MedicalRecord>(record.Id);
                recordE.Rtf = rtf;
                recordE.LastUpdate = DateTime.Now;

                new MedicalRecordMemento().SaveState(recordE);
                session.Update(recordE);

                session.Flush();
            }
        }

        /// <summary>
        /// Unbinds the specified tag from the specified patient.
        /// </summary>
        /// <param name="tag">The tag to unbind.</param>
        /// <param name="fromPatient">From patient.</param>
        public void Unbind(TagDto tag, PatientCardDto fromPatient)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var patientE = session.Get<Patient>(fromPatient.Id);
                var tagE = session.Get<SearchTag>(tag.Id);

                patientE.SearchTags.Remove(tagE);

                session.Update(patientE);
                session.Flush();
            }
        }

        /// <summary>
        /// Unbinds the specified doctor from the specified patient.
        /// </summary>
        /// <param name="doctor">The tag to unbind.</param>
        /// <param name="fromPatient">From patient.</param>
        public void Unbind(DoctorCardDto doctor, PatientCardDto fromPatient)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var patientE = session.Get<Patient>(fromPatient.Id);
                var doctorE = session.Get<Doctor>(doctor.Id);

                patientE.Doctors.Remove(doctorE);
                doctorE.Patients.Remove(patientE);

                session.Update(patientE);
                session.Flush();
            }
        }

        /// <summary>
        /// Updates the patient into the state of the specified patient.
        /// </summary>
        /// <param name="patient">The patient.</param>
        public void Update(ExpandedPatientDto patient)
        {
            using (var updater = new PatientUpdater()) { updater.Update(patient); }
        }

        /// <summary>
        /// Updates the specified macro.
        /// </summary>
        /// <param name="macro">The macro.</param>
        public void Update(MacroDto macro)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var macroE = session.Get<Macro>(macro.Id);
                Mapper.Map<MacroDto, Macro>(macro, macroE);

                session.Update(macroE);
                session.Flush();
            }
        }

        /// <summary>
        /// Updates the name of the record's drawer .
        /// </summary>
        /// <param name="drawer">The drawer.</param>
        public void UpdateRecordDrawerName(RecordDrawerCardDto drawer)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var tagE = session.Get<Tag>(drawer.Id);
                tagE.Name = drawer.Title;

                session.Update(tagE);
                session.Flush();
            }
        }

        /// <summary>
        /// Updates the name of the specified record.
        /// </summary>
        /// <param name="record">The record.</param>
        public void UpdateRecordName(RecordCardDto record)
        {
            using (var session = DalManager.SessionFactory.OpenSession())
            {
                var recordE = session.Get<MedicalRecord>(record.Id);
                recordE.Name = record.Name;

                session.Update(recordE);
                session.Flush();
            }
        }

        private IEnumerable<TResult> Get<TSource, TResult>(SearchCriterion criterion, ISearcher<TSource> searcher)
        {
            using (searcher)
            {
                IQueryable<TSource> query = null;

                if (criterion.StartWith) { query = searcher.FindContainingOr(criterion); }
                else { query = searcher.FindStartWithOr(criterion); }

                return Mapper.Map<IEnumerable<TSource>, IEnumerable<TResult>>(query);
            }
        }

        private IEnumerable<IRule> GetRules()
        {
            var rules = new List<IRule>();
            var types = (from type in Assembly.GetExecutingAssembly().GetTypes()
                         where type.Namespace == "Probel.Ldoc.Dal.Incoherences"
                            && type.IsClass
                            && type.GetInterface("IRule") != null
                         select type);

            foreach (var type in types)
            {
                var rule = Activator.CreateInstance(type) as IRule;
                rules.Add(rule);
            }
            return rules;
        }

        #endregion Methods
    }
}